// Copyright (c) Microsoft Corporation.  All rights reserved.

using System;
using System.Collections.Generic;
using System.Text;
using PfRep.DataEntities;
using System.Reflection;

using Data = PfRep.DataEntities;
using Common = PfRep.Common;
using System.Web;
using System.Diagnostics;
using System.Runtime.InteropServices;

namespace PfRep.DataAccess
{
    /// <summary>
    /// Data access class to query folder data
    /// </summary>
    public static class Exchange
    {
        /// <summary>
        /// Retrieves the number of folders under a given folder (recursively)
        /// </summary>
        /// <param name="connection">Exchange ADO connection</param>
        /// <param name="publicFolderUrl">Root public folder URL</param>
        /// <returns>Number of folders</returns>
        public static int GetNumberOfPublicFolders(ADODB.Connection connection, string publicFolderUrl)
        {
            // Checking if connection is null
            if (connection == null)
            {
                Common.Diagnostics.Trace("Connection is null");
                return 0;
            }

            // Retrieve the folders
            object dummyOutputParameter = Type.Missing;
            ADODB.Recordset folders = 
                connection.Execute("SELECT \"DAV:href\" FROM SCOPE('SHALLOW TRAVERSAL OF \"" +
                publicFolderUrl + "\"')", out dummyOutputParameter, 0);

            // Count them
            int folderCount = 0;
            while (!folders.EOF)
            {
                folderCount++;
                folders.MoveNext();
            }

            return folderCount;
        }

        /// <summary>
        /// Creates a new ADO connection to the given Exchange folder
        /// </summary>
        /// <remarks>It uses the MSDAIPP ADODB provider which uses WebDav inside</remarks>
        /// <param name="folderUrl">The folder's Url to connect to</param>
        /// <returns></returns>
        public static ADODB.Connection CreateConnection(string folderUrl, string userName, string password)
        {
            ADODB.Connection connection = new ADODB.Connection();
            
            // Sets the provider name
            connection.Provider = "msdaipp.dso";
            
            // Opens the connection with the given username or with windows authentication
            if (!string.IsNullOrEmpty(userName))
                connection.Open(folderUrl, userName, password, 0);
            else
                connection.Open(folderUrl, (string)Type.Missing, (string)Type.Missing, 0);

            return connection;
        }

                /// <summary>
        /// Retrieves the subfolders of a given folder
        /// </summary>
        /// <param name="connection"></param>
        /// <param name="folderUrl">Parent Folder's URL</param>
        /// <returns></returns>
        public static List<Data.Folder> GetSubfolders(ADODB.Connection connection, string folderUrl)
        {
            // Checking parameters
            Debug.Assert(connection != null, "Exchange connection is null");
            Debug.Assert(!string.IsNullOrEmpty(folderUrl), "Folder URL is empty");

            // Let's decode the folder URL to make sure that there's no difficulties
            //  at encoding (the MSDAIPP layer accepts normal URLs)
            string correctFolderUrl = HttpUtility.UrlPathEncode(folderUrl);
            Common.Diagnostics.Trace("GetSubfolders", "Decoded Url: " + correctFolderUrl);

            List<Data.Folder> subFolders = new List<Folder>();
            
            // Retrieve the subfolders
            ADODB.Recordset folders;
            try
            {
                object dummyOutputParameter = Type.Missing;
                folders =
                    connection.Execute("SELECT \"DAV:href\", \"DAV:displayname\", \"DAV:visiblecount\" " +
                    "FROM SCOPE('SHALLOW TRAVERSAL OF \"" +
                    correctFolderUrl + "\"') WHERE \"DAV:isfolder\" = True", out dummyOutputParameter, 0);
                Common.Diagnostics.Trace("GetSubfolders", "ADO query completed");
            }
            
            // If there's an exception, we log it and return
            catch (Exception e)
            {
                Common.Diagnostics.Trace("GetSubfolders", "Error during the ADO query: " + e.ToString());
                return subFolders;
            }

            // Convert them to Folder instances
            while (!folders.EOF)
            {
                // Decode the folder's Url to make sure that there's no difficulties
                //  at encoding (the MSDAIPP layer accepts normal URLs)
                string decodedSubfolderUrl = HttpUtility.UrlDecode((string)folders.Fields["DAV:href"].Value, Encoding.UTF7);

                // Create a new folder instance from the recordset's current position
                subFolders.Add(new Data.Folder(0, (string)folders.Fields["DAV:displayname"].Value,
                    decodedSubfolderUrl, false, (int)folders.Fields["DAV:visiblecount"].Value,
                    DateTime.MinValue, "", "", ActionPlan.Archived, "", ""));

                folders.MoveNext();
            }
            Common.Diagnostics.Trace("GetSubfolders", "Folders copied to structure");

            return subFolders;
        }

        /// <summary>
        /// Fills up the content index properties for a given folder
        /// </summary>
        /// <param name="folder"></param>
        /// <param name="connection"></param>
        public static bool FillUpContentIndexProperties(
            Data.Folder folder, 
            ADODB.Connection connection)
        {
            const string dateFieldName = "urn:schemas:httpmail:date";
            const string secondDateFieldName = "urn:schemas:mailheader:date";
            const string thirdDateFieldName = "urn:schemas:httpmail:datereceived";

            // Check parameters
            Debug.Assert(folder != null, "Specified folder is null");
            Debug.Assert(connection != null, "Exchange connection is null");

            Common.Diagnostics.Trace("FillUpContentIndexProperties", 
                "Querying for items under " + folder.Key);
            
            // Retrieve the items reverse ordered by the last modified date
            ADODB.Recordset folders;
            try
            {
                object dummyOutputParameter = Type.Missing;
                folders =
                    connection.Execute("SELECT \"DAV:href\", \"" +
                    dateFieldName + "\", \"" + secondDateFieldName + "\", \"" + thirdDateFieldName + "\" " +
                    "FROM SCOPE('SHALLOW TRAVERSAL OF \"" + folder.Key + "\"') " +
                    "WHERE \"DAV:isfolder\" = False and \"DAV:ishidden\" = False " +
                    "ORDER BY \"" + dateFieldName + "\" DESC",
                    out dummyOutputParameter, 0);
                Common.Diagnostics.Trace("FillUpContentIndexProperties", "ADO query completed");
            }
            
            // If there's an error occurres, we log it and simply return
            catch (Exception e)
            {
                Common.Diagnostics.Trace("FillUpContentIndexProperties", "ADO query failed: " + e.ToString());
                folder.LastModified = DateTime.MinValue;

                // Indicate the the folder couldn't be accessed
                return false;
            }

            // Set the last modified date based on the result above
            if (!folders.EOF)
            {
                Common.Diagnostics.Trace("FillUpContentIndexProperties", "Folder contents non-empty");

                // HACK: Try to retrieve 3 different date time fields. For some reason, MSDAIPP
                //  doesn't give us back some fields, in some conditions ...
                try
                {
                    folder.LastModified = (DateTime)folders.Fields[dateFieldName].Value;
                    Common.Diagnostics.Trace("FillUpContentIndexProperties", "First date field succeeded");
                }
                catch (COMException)
                {
                    Common.Diagnostics.Trace("FillUpContentIndexProperties", 
                        "First date field failed. Trying the second one");
                    
                    try
                    {
                        folder.LastModified = (DateTime)folders.Fields[secondDateFieldName].Value;
                        Common.Diagnostics.Trace("FillUpContentIndexProperties",
                            "Second date field succeeded.");
                    }
                    catch (COMException)
                    {
                        Common.Diagnostics.Trace("FillUpContentIndexProperties",
                            "Second date field failed too. Trying the third (last) one");

                        try
                        {
                            folder.LastModified = (DateTime)folders.Fields[thirdDateFieldName].Value;
                            Common.Diagnostics.Trace("FillUpContentIndexProperties",
                                "Third date field succeeded.");
                        }
                        
                        // If there's no way to retrieve any of the fields we are interested in,
                        //  we mark this folder as not indexed
                        catch (COMException)
                        {
                            Common.Diagnostics.Trace("FillUpContentIndexProperties",
                                "Third date field failed as well. Marking folder as non-indexed.");

                            folder.LastModified = DateTime.MinValue;
                            folder.Indexed = false;
                        }
                    }
                }
            }

            else
                folder.LastModified = DateTime.MinValue;

            // Indicate the the folder filled up
            return true;
        }
    }
}
